package boardgamer.yavalath.client.animation;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import boardgamer.common.client.animation.Animation;
import boardgamer.common.net.PlayerId;
import boardgamer.yavalath.client.RenderHelper;
import boardgamer.yavalath.client.game.YavalathDrawableTile;
import boardgamer.yavalath.common.YavalathPlayerState;

import java.util.Map;

import static com.google.common.base.Preconditions.checkNotNull;

public class SelectedAnimation extends Animation {

  private static final float STROKE_WIDTH_MIN_RADIUS_RATIO = 0.12f;
  private static final int TOTAL_DURATION = 400;
  private static final int PERIOD = 10;
  private static final double ANGLE_60 = Math.PI / 3;

  private static final int ALPHA = 192;

  private static final double MAX_ROTATION = Math.PI;
  private static final double MIN_ROTATION = ANGLE_60 / 2;
  private static final double ROTATION_CHANGE
      = (MIN_ROTATION - MAX_ROTATION) * PERIOD / TOTAL_DURATION;
  private static final double SLOW_ROTATION_CHANGE
      = ANGLE_60 * PERIOD / 200;

  private final double maxRadius;
  private double minRadius;
  private double radiusChange;
  private final Paint paint;

  private boolean isSlow;
  private boolean isOn;
  private double rotation;
  private double radius;
  private double centerX;
  private double centerY;
  private Map<PlayerId, YavalathPlayerState> playerStates;

  private final Path hex;

  public SelectedAnimation(Rect canvasRect) {
    super(PERIOD);
    this.isOn = false;

    int canvasWidth = canvasRect.width();
    int canvasHeight = canvasRect.height();
    this.maxRadius = Math.sqrt(canvasHeight * canvasHeight + canvasWidth * canvasWidth) / 2;
    this.hex = new Path();
    this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    this.paint.setStyle(Paint.Style.STROKE);
  }

  public void init(Map<PlayerId, YavalathPlayerState> playerStates) {
    this.playerStates = checkNotNull(playerStates, "playerStates");
  }

  public void setSelected(YavalathDrawableTile selectedTile, PlayerId player) {
    checkNotNull(playerStates, "Cannot call setSelected before calling init");
    checkNotNull(selectedTile, "selectedTile");

    this.centerX = selectedTile.getBoundingRect().centerX();
    this.centerY = selectedTile.getBoundingRect().centerY();

    this.rotation = MAX_ROTATION;

    this.minRadius
        = selectedTile.getBoundingRect().width() / 2 * (1 + STROKE_WIDTH_MIN_RADIUS_RATIO);
    this.paint.setStrokeWidth((float) minRadius * STROKE_WIDTH_MIN_RADIUS_RATIO);

    this.radiusChange = (minRadius - maxRadius) * PERIOD / TOTAL_DURATION;
    this.radius = maxRadius;
    this.isOn = true;
    this.isSlow = false;

    this.paint.setColor(RenderHelper.instance().getColor(playerStates.get(player).getColor()));

    setPoints();
  }

  private void setPoints() {
    hex.reset();
    for (int i = 0; i < 6; i++) {
      double currentRotation = rotation + ANGLE_60 * i;
      float x = (float) (centerX + radius * Math.sin(currentRotation));
      float y = (float) (centerY + radius * Math.cos(currentRotation));
      if (i == 0) {
        hex.moveTo(x, y);
      } else {
        hex.lineTo(x, y);
      }
    }
    hex.close();
  }

  public void turnOff() {
    this.isOn = false;
  }

  @Override
  public boolean isDead() {
    return false;
  }

  @Override
  public void draw(Canvas canvas) {
    if (!isOn) {
      return;
    }

    this.paint.setAlpha(ALPHA);
    canvas.drawPath(hex, paint);
  }

  @Override
  protected void onPeriodic() {
    if (!isOn) {
      return;
    }

    if (isSlow) {
      rotation -= SLOW_ROTATION_CHANGE;
      if (rotation < 0) {
        rotation += Math.PI * 2;
      }
    } else {
      rotation += ROTATION_CHANGE;
      radius = Math.max(radius + radiusChange, minRadius);
      isSlow = rotation < MIN_ROTATION;
    }
    setPoints();
  }
}
